import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * 阿里巴巴规范
 * 【强制】在使用阻塞等待获取锁的方式中，必须在 try 代码块之外，并且在加锁方法与 try 代
 * 码块之间没有任何可能抛出异常的方法调用，避免加锁成功后，在 finally 中无法解锁。
 * 说明一：如果在 lock 方法与 try 代码块之间的方法调用抛出异常，那么无法解锁，造成其它线程无法成功
 * 获取锁。
 * 说明二：如果 lock 方法在 try 代码块之内，可能由于其它方法抛出异常，导致在 finally 代码块中，
 * unlock 对未加锁的对象解锁，它会调用 AQS 的 tryRelease 方法（取决于具体实现类），抛出
 * IllegalMonitorStateException 异常。
 */
class Ticket {  // 资源类 = 实例变量+实例方法
    private int number = 30;
    Lock lock = new ReentrantLock();

    public void sale() {
        // 快捷键trylock
        lock.lock();
        try {
            if(number > 0) {
                System.out.println(Thread.currentThread().getName() + "\t卖出第：" + (number--) + "\t还剩下：" + number);
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }

    }
}

public class SaleTicketDemo01 {
    public static void main(String[] args) {
        Ticket ticket = new Ticket();

        new Thread(() -> {for (int i =1;i<=40;i++) ticket.sale();},"A").start();
        new Thread(() -> {for (int i =1;i<=40;i++) ticket.sale();},"B").start();
        new Thread(() -> {for (int i =1;i<=40;i++) ticket.sale();},"C").start();


        // 线程的6种状态
        // Thread.State
        // start后没有开始，只是等待系统调用，真正开始由CPU决定
//        new Thread(new Runnable() {
//            @Override
//            public void run() {
//                for (int i = 0; i < 20; i++) {
//                    ticket.sale();
//                }
//            }
//        }, "A").start();
//        new Thread(new Runnable() {
//            @Override
//            public void run() {
//                for (int i = 0; i < 20; i++) {
//                    ticket.sale();
//                }
//            }
//        }, "B").start();
        new Thread(new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i < 20; i++) {
                    ticket.sale();
                }
            }
        }, "C").start();


//        Thread t1 = new Thread();
//        Thread t2 = new Thread();
//        Thread t3 = new Thread();
    }
}
